package com.lambkit.db.hutool;

import cn.hutool.core.lang.func.VoidFunc1;
import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Db;
import cn.hutool.db.Entity;
import cn.hutool.db.transaction.TransactionLevel;
import com.lambkit.db.*;
import com.lambkit.db.dialect.IDialect;
import com.lambkit.db.sql.Columns;
import com.lambkit.db.sql.Example;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
 * @author yangyong(孤竹行)
 */
public abstract class HutoolDbOpt<T extends IRowData, P extends IPageData<T>> extends AbstractDbOpt<T, P> {
    private Db htDb;
    private IDialect dialect;

    public HutoolDbOpt(Db htDb, IDialect dialect)  {
        super(dialect);
        this.htDb = htDb;
    }
    public Db getDb() {
        return htDb;
    }

    public abstract Entity toEntity(T record);

    private List<Entity> toEntity( List<T> rowDatas) {
        List<Entity> entities = new ArrayList<>(rowDatas.size());
        for(T rowData : rowDatas) {
            entities.add(toEntity(rowData));
        }
        return entities;
    }

    //public abstract P paginate(Integer pageNumber, Integer pageSize, Sql sqlPara);

    @Override
    public Long count(Sql sqlPara) {
        try {
            Number number = htDb.queryNumber(sqlPara.getSql(), sqlPara.getPara());
            return number != null ? number.longValue() : 0;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean save(T record) {
        try {
            Entity entity = toEntity(record);
            int flag = htDb.insert(entity);
            record.put(entity);
            return flag == 1 ? true : false;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean delete(T record) {
        try {
            int flag = htDb.del(toEntity(record));
            return flag == 1 ? true : false;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public boolean update(T record) {
        try {
            Entity entity = toEntity(record);
            int flag = htDb.update(entity, Entity.create());
            record.put(entity);
            return flag == 1 ? true : false;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int update(String sql, Object... paras) {
        try {
            return htDb.execute(sql, paras);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //public abstract List<T> find(String sql, Object... paras);

    @Override
    public T findById(String table, String primaryKeys, Object... idValues) {
        if(idValues == null || idValues.length == 0) {
            return null;
        }
        Columns columns = Columns.create();
        if(StrUtil.isNotBlank(primaryKeys)) {
            String[] keys = primaryKeys.split(",");
            if(keys.length != idValues.length) {
                throw new IllegalArgumentException("id values error, need " + keys.length + " id value");
            }
            for(int i=0; i< keys.length; i++) {
                columns.eq(keys[i].trim(), idValues[i]);
            }
        } else {
            columns.eq("id", idValues[0]);
        }
        Example example = Example.create(table, columns);
        return findFirst(example);
    }

    @Override
    public int delete(String sql, Object... paras) {
        try {
            return htDb.execute(sql, paras);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //public abstract boolean tx(Integer transactionLevel, ITxAction<V> atom);


    @Override
    public <V extends IDbOpt> boolean tx(Integer transactionLevel, ITxAction<V> atom) {
        V dbOpt = (V) this;
        TransactionLevel level = transactionLevel!=null ? TransactionLevel.values()[transactionLevel] : null;
        try {
            getDb().tx(level, new VoidFunc1<Db>() {
                @Override
                public void call(Db db) throws Exception {
                    atom.execute(dbOpt);
                }
            });
            return true;
        } catch (SQLException e) {
            return false;
        }
    }

    @Override
    public int[] batch(String sql, Object[][] paras, int batchSize) {
        Iterable<Object[]> iterable = Arrays.asList(paras);
        try {
            return htDb.executeBatch(sql, iterable);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int[] batch(List<String> sqlList, int batchSize) {
        try {
            return htDb.executeBatch(sqlList);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int[] batchSave(List<T> recordList, int batchSize) {
        try {
            return htDb.insert(toEntity(recordList));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int[] batchUpdate(List<T> recordList, int batchSize) {
        try {
            //yangyong：暂时这样，待测试
            return htDb.insert(toEntity(recordList));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}
